package org.p2psimulator.entity.protocol.structured.chordcycles;

import java.util.HashSet;
import java.util.List;

import org.p2psimulator.entity.Id;
import org.p2psimulator.entity.Message;
import org.p2psimulator.entity.Peer;
import org.p2psimulator.entity.id.DoubleId;
import org.p2psimulator.entity.overlaynetwork.ChordOverlay;
import org.p2psimulator.entity.overlaynetwork.routingtable.ChordFingerTable;
import org.p2psimulator.entity.protocol.messages.MessageTypes;
import org.p2psimulator.entity.protocol.messages.chord.FindSucessor;
import org.p2psimulator.entity.protocol.messages.chord.SucessorHere;
import org.p2psimulator.engine.Environment;

public class ChordFixFingersCycle extends ChordCycle {

    private HashSet<DoubleId> fingersRequestSet;

    public ChordFixFingersCycle(Environment env, int transId) {
        super(env, transId);
    }

    @Override
    public String runRequestCycle(Peer p) {

        List<Message> sucessorList = p.getMessageStack().get(
                MessageTypes.CHORD_SUCESSORHERE.getName());
        sucessorList.clear(); // para evitar que mensagens anteriores interfiram
        
        StringBuilder str = new StringBuilder("  [FIX FINGERS CYCLE]   -   ");
        ChordFingerTable table = getFingerTable(p);
        ChordOverlay overlay = (ChordOverlay) env.getChordOverlayNetwork();

        if (fingersRequestSet == null) {
            fingersRequestSet = new HashSet<DoubleId>();
        } else {
            fingersRequestSet.clear();
        }
        Double n = (Double) table.getOwner().getValue();
        int m = overlay.getM(); // address space

        if (!table.isSucessorOnline()) {

            Id online = overlay.getOnlineRandomPeer().getId();
            FindSucessor findSucessor = new FindSucessor(p.getId(), online,
                    currentTransactionId, p.getBehavior().getBehaviorType());
            findSucessor.setReferredId(p.getId());
            findSucessor.setTTL(255);
            findSucessor.setHOP(0);
            findSucessor.setServentId(p.getId());

            env.getNetwork().sendMessage(findSucessor);
            fingersRequestSet.add((DoubleId) p.getId());
        }
        int i = 0;
        for (i = 0; i <= m; i++) {

            Double entry = (n + Math.pow(2, i - 1)) % Math.pow(2, m);

            DoubleId entryId = new DoubleId(entry.intValue());

            FindSucessor findSucessor = new FindSucessor(p.getId(), table.getSucessor(), currentTransactionId, p.getBehavior().getBehaviorType());
            findSucessor.setReferredId(entryId);
            findSucessor.setTTL(7);
            findSucessor.setHOP(0);
            findSucessor.setServentId(p.getId());

            env.getNetwork().sendMessage(findSucessor);
            fingersRequestSet.add(entryId);
        }
        str.append("[ FINDSUCESSOR enviados: " + i + " ]");
        return str.toString();
    }

    @Override
    public String runResponseCycle(Peer p) {
        StringBuilder str = new StringBuilder("  [FIX FINGERS CYCLE]   -  ");

        ChordFingerTable table = getFingerTable(p);
        List<Message> sucessorList = p.getMessageStack().get(
                MessageTypes.CHORD_SUCESSORHERE.getName());
        if (sucessorList != null & sucessorList.size() > 0) {

            str.append(" [ TOTAL DE SUCESSORES RECEBIDOS: "
                    + sucessorList.size() + " ]");
            table.clear();
            for (Message msg : sucessorList) {
                SucessorHere sucHere = (SucessorHere) msg;

                if (fingersRequestSet.contains((DoubleId) sucHere.getReferredId())) {
                    table.insertNode(sucHere.getSucessorId(),sucHere.getSucessorId());
                }
            }

            fingersRequestSet.clear();
            fingersRequestSet = null;
        } else {
            str.append("[ TOTAL DE SUCESSORES RECEBIDOS: 0 ]");
        }

        return str.toString();

    }

    @Override
    public String runSecondRequestCycle(Peer p) {
        // TODO Auto-generated method stub
        return "";
    }

    @Override
    public String runSecondResponseCycle(Peer p) {
        // TODO Auto-generated method stub
        return "";
    }
}
